# Copyright 2007 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


import datetime
import time

import gvn.changebranch
import gvn.util


class RevisionDescriptionImpl(object):
  def __init__(self, config, project, revnum):
    # May raise errrors.NonExistentRevision; let it go.
    (self._revision,) = project.repository.GetRevisions([''], revnum)

  def Revision(self):
    return self._revision.number

  def Username(self):
    return self._revision['svn:author']

  def Date(self):
    return self._revision['svn:date']

  def ChangeBranchName(self):
    return None

  def Description(self):
    return self._revision['svn:log']

  def AffectedPaths(self):
    return [(x.path, x.action) for x in self._revision.paths]


class ChangeBranchDescriptionImpl(object):
  def __init__(self, config, project, change_name):
    (username, name, revision) = gvn.util.ParseChangeName(change_name)

    self._cb = gvn.changebranch.ChangeBranch(config, project, name, username,
                                             revision)

  def Revision(self):
    return self._cb.revision

  def Username(self):
    return self._cb.username

  def Date(self):
    return self._cb.revision['svn:date']

  def ChangeBranchName(self):
    return self._cb.change_name

  def Description(self):
    return self._cb.description

  def AffectedPaths(self):
    for i in self._cb.changed_paths:
      yield (i.source_path, i.action)


class Description(object):
  """A utility class to unify description formats.

  Instantiate objects of this class with either a change branch
  name or a revision number.
  """

  def __init__(self, config, project, change_or_revision):
    self._impl = None
    revnum = gvn.util.MatchRevision(change_or_revision)
    if revnum is not None:
      self._impl = RevisionDescriptionImpl(config, project, int(revnum))
    else:
      self._impl = ChangeBranchDescriptionImpl(config, project, change_or_revision)

  def _TZHours(self):
    return time.timezone/60/60

  def _TZSeconds(self):
    return time.timezone - self._TZHours()*3600

  def _TZOffsetString(self):
    sign = '-'
    if time.timezone < 0:
      sign = '+'
    return "%s%02d%02d" % (sign, self._TZHours(), self._TZSeconds())

  def Author(self):
    # I would expect a KeyError when authz prevents the user from seeing
    # the revision, but instead we get back None; cover both cases.
    try:
      author = self._impl.Username()
    except KeyError:
      author = None
    if author is None:
      return '(no author)'
    return author

  def Date(self):
    # See Author comment about KeyError vs. returning None.
    try:
      date = self._impl.Date()
    except KeyError:
      date = None
    if date is None:
      return '(no date)'

    dateobj = gvn.util.ConvertStringDateToDateTime(date)
    dateobj -= datetime.timedelta(seconds=time.timezone)
    datestring = dateobj.strftime("%Y-%m-%d %H:%M:%S %%s (%a, %d %b %Y)")
    return datestring % (self._TZOffsetString(),)

  def Log(self):
    log = self._impl.Description()
    if log is None:
      return ''
    return log

  def Output(self):
    rev = "r%s" % self._impl.Revision()
    if self._impl.ChangeBranchName():
      rev = '*' + self._impl.ChangeBranchName()

    header = ' | '.join([rev, self.Author(), self.Date()])

    affected_paths = self._impl.AffectedPaths()
    affected_path_string = '\n'.join(["   %s /%s" % (a, p)
                                      for (p, a) in affected_paths])
    return '\n'.join([
      header,
      '',
      'Description:',
      '',
      self.Log(),
      '',
      '',
      'Affected Paths:',
      affected_path_string,
      '',
      ])
